package com.db.protocol;

import java.util.HashMap;
import java.util.Map;

import com.db.protocol.exception.IncorrectValueProtocolException;
import com.db.protocol.exception.InvalidMessageProtocolException;
import com.db.protocol.exception.InvalidTagProtocolException;
import com.db.protocol.exception.NotParsingValueProtocolException;
import com.db.protocol.exception.ParseProtocolException;
import com.db.protocol.exception.TagMissingProtocolException;
import com.db.protocol.tags.MsgType;
import com.db.protocol.tags.OrderSide;
import com.db.protocol.tags.OrderStatus;
import com.db.protocol.tags.OrderType;
import com.db.protocol.tags.RejectReason;
import com.db.protocol.tags.Tag;
import com.db.data.Order;
import com.db.data.Price;

public class ProtocolNullFix extends AbstractProtocol implements ProtocolServer, ProtocolClient {

	protected Map<Tag, Object> parseMsgLogon(Map<Tag, String> pairs) throws ParseProtocolException {
		Map<Tag, Object> values = new HashMap<Tag, Object>();
		
		values.put(Tag.MSG_TYPE, MsgType.LOGON);
		if (pairs.containsKey(Tag.CLIENT_ID)) {
			values.put(Tag.CLIENT_ID, pairs.get(Tag.CLIENT_ID));
		}
		
		return values;
	}
	
	protected Map<Tag, Object> parseMsgLogout(Map<Tag, String> pairs) throws ParseProtocolException {
		Map<Tag, Object> values = new HashMap<Tag, Object>();
		
		values.put(Tag.MSG_TYPE, MsgType.LOGOUT);
		if (pairs.containsKey(Tag.TEXT)) {
			values.put(Tag.TEXT, pairs.get(Tag.TEXT));
		}
		
		return values;
	}	

	protected Map<Tag, Object> parseMsgNewTrade(Map<Tag, String> pairs) throws ParseProtocolException {
		Map<Tag, Object> values = new HashMap<Tag, Object>();
		
		values.put(Tag.MSG_TYPE, MsgType.NEW_TRADE);
		if (!pairs.containsKey(Tag.ORDER_ID)) {
			throw new TagMissingProtocolException(Tag.ORDER_ID);
		}
		values.put(Tag.ORDER_ID, pairs.get(Tag.ORDER_ID));
		
		if (!pairs.containsKey(Tag.TICKER_SYMBOL)) {
			throw new TagMissingProtocolException(Tag.TICKER_SYMBOL);
		}
		values.put(Tag.TICKER_SYMBOL, pairs.get(Tag.TICKER_SYMBOL));
		
		if (!pairs.containsKey(Tag.ORDER_SIDE)) {
			throw new TagMissingProtocolException(Tag.ORDER_SIDE);
		}
		values.put(Tag.ORDER_SIDE, OrderSide.parse(pairs.get(Tag.ORDER_SIDE)));
		
		if (!pairs.containsKey(Tag.NUMBER_OF_SHARES)) {
			throw new TagMissingProtocolException(Tag.NUMBER_OF_SHARES);
		}
		try {
			values.put(Tag.NUMBER_OF_SHARES, Integer.valueOf(pairs.get(Tag.NUMBER_OF_SHARES)));
		} catch (NumberFormatException exception) {
			throw new NotParsingValueProtocolException(Tag.NUMBER_OF_SHARES);
		}
		
		if (!pairs.containsKey(Tag.ORDER_TYPE)) {
			throw new TagMissingProtocolException(Tag.ORDER_TYPE);
		}
		OrderType type = OrderType.parse(pairs.get(Tag.ORDER_TYPE));
		values.put(Tag.ORDER_TYPE, type);
		
		if (type == OrderType.LIMIT) {
			if (!pairs.containsKey(Tag.PRICE)) {
				throw new TagMissingProtocolException(Tag.PRICE);					
			}
			
			try {
				values.put(Tag.PRICE, new Price(pairs.get(Tag.PRICE)));
			} catch (NumberFormatException exception) {
				throw new NotParsingValueProtocolException(Tag.PRICE);
			}
		}
		
		return values;
	}

	protected Map<Tag, Object> parseMsgRequestCancelTrade(Map<Tag, String> pairs) throws ParseProtocolException {
		Map<Tag, Object> values = new HashMap<Tag, Object>();
		
		values.put(Tag.MSG_TYPE, MsgType.REQUEST_CANCEL_TRADE);
		if (pairs.containsKey(Tag.ORDER_ID)) {
			values.put(Tag.ORDER_ID, pairs.get(Tag.ORDER_ID));
		} else {
			throw new TagMissingProtocolException(Tag.ORDER_ID);
		}

		return values;
	}
	
	protected Map<Tag, Object> parseMsgReport(Map<Tag, String> pairs) throws ParseProtocolException {
		Map<Tag, Object> values = new HashMap<Tag, Object>();
		
		values.put(Tag.MSG_TYPE, MsgType.REPORT);
		if (pairs.containsKey(Tag.ORDER_ID)) {
			values.put(Tag.ORDER_ID, pairs.get(Tag.ORDER_ID));
		} else {
			throw new TagMissingProtocolException(Tag.ORDER_ID);
		}
		
		if (!pairs.containsKey(Tag.ORDER_STATUS)) {
			throw new TagMissingProtocolException(Tag.TICKER_SYMBOL);
		}
		
		OrderStatus status = OrderStatus.parse(pairs.get(Tag.ORDER_STATUS));
		values.put(Tag.ORDER_STATUS, status);
		if (status == OrderStatus.REJECTED) {
			if (pairs.containsKey(Tag.TEXT)) {
				values.put(Tag.TEXT, pairs.get(Tag.TEXT));
			} else {
				throw new TagMissingProtocolException(Tag.TEXT);
			}				
		} else if (status.equals(OrderStatus.PARTIALLY_FILLED) || status.equals(OrderStatus.FILLED)) {
			if (!pairs.containsKey(Tag.LAST_PRICE)) {
				throw new TagMissingProtocolException(Tag.LAST_PRICE);
			}
			try {
				values.put(Tag.LAST_PRICE, new Price(pairs.get(Tag.LAST_PRICE)));
			} catch (NumberFormatException exception) {
				throw new NotParsingValueProtocolException(Tag.LAST_PRICE);
			}
			
			if (!pairs.containsKey(Tag.LAST_QTY)) {
				throw new TagMissingProtocolException(Tag.LAST_QTY);
			}
			try {
				values.put(Tag.LAST_QTY, Integer.valueOf(pairs.get(Tag.LAST_QTY)));
			} catch (NumberFormatException exception) {
				throw new NotParsingValueProtocolException(Tag.LAST_QTY);
			}
			
			if (!pairs.containsKey(Tag.PARTY_ID)) {
				throw new TagMissingProtocolException(Tag.PARTY_ID);
			}
			values.put(Tag.PARTY_ID, pairs.get(Tag.PARTY_ID));
		}
		
		return values;
	}
	
	protected Map<Tag, Object> parseMsgReject(Map<Tag, String> pairs) throws ParseProtocolException {
		Map<Tag, Object> values = new HashMap<Tag, Object>();
		
		values.put(Tag.MSG_TYPE, MsgType.REJECT);
		if (pairs.containsKey(Tag.REJECT_REASON)) {
			values.put(Tag.REJECT_REASON, RejectReason.parse(pairs.get(Tag.REJECT_REASON)));
		} else {
			throw new TagMissingProtocolException(Tag.REJECT_REASON);
		}
		if (pairs.containsKey(Tag.TEXT)) {
			values.put(Tag.TEXT, pairs.get(Tag.TEXT));
		}	else {
			throw new TagMissingProtocolException(Tag.TEXT);
		}

		return values;
	}
	
	public Map<Tag, Object> parseMessage(String message) throws ParseProtocolException {		
		Map<Tag, String> pairs = parse(message);
		
		if (!pairs.containsKey(Tag.MSG_TYPE)) {
			throw new TagMissingProtocolException(Tag.MSG_TYPE);
		}
		
		MsgType type = MsgType.parse(pairs.get(Tag.MSG_TYPE));
		
		switch (type) {
			case LOGON:
				return parseMsgLogon(pairs);
			case NEW_TRADE:
				return parseMsgNewTrade(pairs);
			case LOGOUT:
				return parseMsgLogout(pairs);
			case REQUEST_CANCEL_TRADE:
				return parseMsgRequestCancelTrade(pairs);
			case REJECT:
				return parseMsgReject(pairs);
			case REPORT:
				return parseMsgReport(pairs);
			default:
				throw new InvalidMessageProtocolException(message);
		}
	}

	
	
	@Override
	public String msgReportLogon() {
		StringBuilder msg = newMsg(MsgType.LOGON);
		return msg.toString();
	}

	@Override
	public String msgReportLogout() {
		StringBuilder msg = newMsg(MsgType.LOGOUT);
		return msg.toString();
	}

	@Override
	public String msgAck(Order order) {
		StringBuilder msg = newMsg(MsgType.REPORT);
		msg = addPair(msg, Tag.ORDER_ID, order.getOrderID());
		msg = addPair(msg, Tag.ORDER_STATUS, String.valueOf(OrderStatus.NEW.value()));
		return msg.toString();
	}

	@Override
	public String msgPartiallyFilled(Order order, Price lastPrice, int lastCount, String otherClientID) {
		StringBuilder msg = newMsg(MsgType.REPORT);
		msg = addPair(msg, Tag.ORDER_ID, order.getOrderID());
		msg = addPair(msg, Tag.LAST_PRICE, lastPrice.toString());
		msg = addPair(msg, Tag.LAST_QTY, String.valueOf(lastCount));
		msg = addPair(msg, Tag.PARTY_ID, otherClientID);
		msg = addPair(msg, Tag.ORDER_STATUS, String.valueOf(OrderStatus.PARTIALLY_FILLED.value()));
		return msg.toString();
	}

	@Override
	public String msgFilled(Order order, Price lastPrice, int lastCount, String otherClientID) {
		StringBuilder msg = newMsg(MsgType.REPORT);
		msg = addPair(msg, Tag.ORDER_ID, order.getOrderID());
		msg = addPair(msg, Tag.LAST_PRICE, lastPrice.toString());
		msg = addPair(msg, Tag.LAST_QTY, String.valueOf(lastCount));
		msg = addPair(msg, Tag.PARTY_ID, otherClientID);
		msg = addPair(msg, Tag.ORDER_STATUS, String.valueOf(OrderStatus.FILLED.value()));
		return msg.toString();
	}

	@Override
	public String msgCancelled(Order order) {
		StringBuilder msg = newMsg(MsgType.REPORT);
		msg = addPair(msg, Tag.ORDER_ID, order.getOrderID());
		msg = addPair(msg, Tag.ORDER_STATUS, String.valueOf(OrderStatus.CANCELLED.value()));
		return msg.toString();
	}

	@Override
	public String msgRejectedReport(Order order, String error) {
		StringBuilder msg = newMsg(MsgType.REPORT);
		msg = addPair(msg, Tag.ORDER_ID, order.getOrderID());
		msg = addPair(msg, Tag.TEXT, error);
		msg = addPair(msg, Tag.ORDER_STATUS, String.valueOf(OrderStatus.REJECTED.value()));
		return msg.toString();		
	}

	@Override
	public String msgRejected(RejectReason reason, String error) {
		StringBuilder msg = newMsg(MsgType.REJECT);
		msg = addPair(msg, Tag.REJECT_REASON, String.valueOf(reason.value()));
		msg = addPair(msg, Tag.TEXT, error);
		return msg.toString();
	}
	
	@Override
	public String msgRejected(ParseProtocolException exception) {
		if (exception instanceof IncorrectValueProtocolException) {
			return msgRejected(RejectReason.INCORRECT_VALUE, "Incorrect value of tag: " + exception.getMessage());
		} else if (exception instanceof InvalidMessageProtocolException) {
			return msgRejected(RejectReason.INVALID_MSG, "Invalid structure of message: " + exception.getMessage());
		} else if (exception instanceof InvalidTagProtocolException) {
			return msgRejected(RejectReason.INVALID_TAG, "Unknowns tag: " + exception.getMessage());
		} else if (exception instanceof NotParsingValueProtocolException) {
			return msgRejected(RejectReason.NOT_PARSING_VALUE, "Error parsing value of tag: " + exception.getMessage());
		} else if (exception instanceof TagMissingProtocolException) {
			return msgRejected(RejectReason.TAG_MISSING, "Tag not found: " + exception.getMessage());
		}
		return msgRejected(RejectReason.INVALID_MSG, "Unknows exception: " + exception.getMessage());
	}
	
	
	
	@Override
	public String msgLogon(String clientID) {
		StringBuilder msg = newMsg(MsgType.LOGON);
		return addPair(msg, Tag.CLIENT_ID, clientID).toString();
	}

	@Override
	public String msgLogout() {
		StringBuilder msg = newMsg(MsgType.LOGOUT);
		return msg.toString();
	}

	@Override
	public String msgNewTrade(Order order) {
		StringBuilder msg = newMsg(MsgType.NEW_TRADE);
		msg = addPair(msg, Tag.ORDER_ID, order.getOrderID());
		msg = addPair(msg, Tag.TICKER_SYMBOL, order.getTicketSymbol());
		if (order.getSide() == OrderSide.BUY) {
			msg = addPair(msg, Tag.ORDER_SIDE, String.valueOf(OrderSide.BUY.value()));
		} else {
			msg = addPair(msg, Tag.ORDER_SIDE, String.valueOf(OrderSide.SELL.value()));
		}
		msg = addPair(msg, Tag.NUMBER_OF_SHARES, String.valueOf(order.getQuantity()));
		if (order.getType() == OrderType.LIMIT) {
			msg = addPair(msg, Tag.PRICE, String.valueOf(order.getPrice()));
			msg = addPair(msg, Tag.ORDER_TYPE, String.valueOf(OrderType.LIMIT.value()));
		} else {
			msg = addPair(msg, Tag.ORDER_TYPE, String.valueOf(OrderType.MARKET.value()));			
		}
		return msg.toString();
	}

	@Override
	public String msgRequestCancelTrade(Order order) {
		StringBuilder msg = newMsg(MsgType.REQUEST_CANCEL_TRADE);
		msg = addPair(msg, Tag.ORDER_ID, order.getOrderID());
		return msg.toString();
	}

}
